package lzupdater.locale;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;

import lzupdater.log.Logger;

//LZRosterUpdater, a java client to update LZRoster
//Copyright (C) 2008 Fraskor from Krasus (e-mail: fraskor@gmail.com)

//This file is part of LZRosterUpdater.

//LZRosterUpdater is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.

/**
 * 
 * @author Fraskor
 */
public class Dictionary {

	public final static String DEFAULT_DICTIONARY = "locale/Dictionary.properties";

	private static final String systemEncoding = System.getProperty("sun.jnu.encoding");
	private static final Locale localeOfKey = new Locale("enUS");
	private static Dictionary singleton;

	private Logger logger;

	private ArrayList<LocaleListener> localeListerners;

	private boolean initialized;
	private boolean errorWhenInitialized;

	private HashMap<String, Translation> data;
	private Locale currentLocale;

	private HashMap<Locale, Integer> availableLanguages = new HashMap<Locale, Integer>();

	private Dictionary() {
		singleton = this;
		localeListerners = new ArrayList<LocaleListener>();
		initialized = false;
		errorWhenInitialized = false;
		data = new HashMap<String, Translation>();
		currentLocale = Locale.getUserLocale();
	}

	/**
	 * Fill the dictionnary with the data found in the inputStream
	 * 
	 * @param dictionnary
	 *            the dictionnary inputStream
	 */
	public static void load(InputStream dictionnary) {
		getSingleton()._load(dictionnary);
	}

	private void _load(InputStream dictionnary) {

		errorWhenInitialized = false;

		HashSet<String> overridedKeys = new HashSet<String>();

		BufferedReader b;
		try {
			b = new BufferedReader(
					new InputStreamReader(dictionnary,"UTF8"));
		} catch (UnsupportedEncodingException e) {
			b = new BufferedReader(
					new InputStreamReader(dictionnary));
		}
		String line = null;
		data.clear();

		Translation translation = null;
		String key = null;

		try {
			while ((line = b.readLine()) != null) {
				if (line.trim().startsWith("#"))
					continue;
				int idx = line.indexOf("=");
				if (idx >= 0) {
					Locale l = null;
					String k = line.substring(0, idx).trim();
					String v = line.substring(idx + 1).trim();

					if (k.equalsIgnoreCase("key")) {
						key = v.trim();
						translation = data.get(key);
						if (translation == null) {
							translation = new Translation();
							data.put(key, translation);
						}
						l = localeOfKey;
					} else {
						l = new Locale(k);
						v = new String(v.getBytes(systemEncoding));
					}

					if (key != null) {
						if (translation.setTranslation(l, v)) {
							overridedKeys.add(key);
						} else {
							Integer nbTranslations = availableLanguages.get(l);
							nbTranslations = nbTranslations == null ? 1
									: nbTranslations++;
							availableLanguages.put(l, nbTranslations);
						}
					}
				}
			}
			b.close();
		} catch (IOException e) {
			if (logger == null) {
				System.err.println("Error when initializing the Dictionnary !");
				for (StackTraceElement ste : e.getStackTrace()) {
					System.err.println(ste);
				}
			}
			else {
				logger.error("Error when initializing the Dictionnary !");
				for (StackTraceElement ste : e.getStackTrace()) {
					logger.error(ste);
				}
			}
			errorWhenInitialized = true;
		}

		initialized = true;
	}

	/**
	 * @param locale
	 * @param key
	 * @return the translation in the locale 'locale' for the element with key
	 *         value 'key'
	 */
	public static String translate(Locale locale, String key) {
		if (key == null) return "";

		key = key.trim();

		if (!getSingleton().initialized) {
			initialiseWithDefault();
		}

		Translation translations = getSingleton().data.get(key);
		String resultat = null;
		if (translations != null) {
			resultat = translations.getTranslation(locale);
		}

		return resultat == null ? key : resultat;
	}

	/**
	 * @param key
	 * @return
	 */
	public static String translate(String key) {
		return translate(getSingleton().currentLocale, key);
	}

	public static void addLocaleListener(LocaleListener l) {
		if (!getSingleton().localeListerners.contains(l)) {
			getSingleton().localeListerners.add(l);
			l.localeChange(singleton.currentLocale);
		}
	}

	public static void setLogger(Logger logger) {
		getSingleton().logger = logger;
	}

	public static void setCurrentLanguage(Locale locale) {
		if (locale == null)
			return;

		for (LocaleListener l : singleton.localeListerners) {
			l.localeChange(locale);
		}
		getSingleton().currentLocale = locale;
	}

	private static void initialiseWithDefault() {
		load(Dictionary.class.getClassLoader().getResourceAsStream(
		"locale/Dictionary.properties"));
	}

	public static boolean isErrorWhenInitialized() {
		return singleton.errorWhenInitialized;
	}

	public static Dictionary getSingleton() {
		if (singleton == null) {
			new Dictionary();
		}
		return singleton;
	}

	public static Locale getCurrentLocale() {
		return getSingleton().currentLocale;
	}

	public static void dump(String outputFile) {
		try {
			HashSet<String> keyMemo = new HashSet<String>();

			PrintStream ps = new PrintStream(new FileOutputStream(outputFile));

			for (Entry<String, Translation> entry : getSingleton().data
					.entrySet()) {
				String key = entry.getKey().trim();
				if (!keyMemo.contains(key)) {
					keyMemo.add(key);
					ps.format("key = %s\n", key);
					for (Entry<String, HashMap<String, String>> transLang : entry
							.getValue().entrySet()) {
						for (Entry<String, String> trans : transLang.getValue()
								.entrySet()) {
							ps.format("%s%s = %s\n", transLang.getKey(), trans
									.getKey(), trans.getValue());
						}
					}
				}
				ps.println();
			}

			for (LocaleListener l : getSingleton().localeListerners) {
				String key = l.getKey();
				if (key == null)
					continue;
				key = key.trim();
				if (!getSingleton().data.containsKey(key)
						&& !keyMemo.contains(key)) {
					keyMemo.add(key);
					ps.format("key = %s\n\n", key);
				}
			}
		} catch (FileNotFoundException e) {
			//
		}
	}

	private LanguageItem[] _getAvailableLanguages(boolean withCompleteFlag) {
		int nbKeys = data.size();

		LanguageItem[] resultat = new LanguageItem[availableLanguages.size()];
		int idx = 0;
		for (Entry<Locale, Integer> entry : availableLanguages.entrySet()) {
			Locale l = entry.getKey();
			resultat[idx++] = new LanguageItem(l, (withCompleteFlag && entry
					.getValue() == nbKeys));
		}
		return resultat;
	}

	public static LanguageItem[] getAvailableLanguages(boolean withCompleteFlag) {
		return getSingleton()._getAvailableLanguages(withCompleteFlag);
	}

	public static LanguageItem[] getAvailableLanguages() {
		return getSingleton()._getAvailableLanguages(true);
	}

	public static class LanguageItem {
		private final Locale locale;
		private final boolean flag;

		public LanguageItem(Locale locale, boolean flag) {
			this.locale = locale;
			this.flag = flag;
		}

		@Override
		public String toString() {
			return String.format("%s%s (%s)", locale, flag ? "*" : "",
					translate(locale, "_LANGUAGE_NAME_"));
		}

		public Locale getLocale() {
			return locale;
		}

		public boolean isFlag() {
			return flag;
		}

		@Override
		public boolean equals(Object obj) {
			if (obj instanceof String) {
				return obj.equals(locale.toString());
			} else if (obj instanceof Locale) {
				return obj.equals(locale);
			} else if (obj instanceof LanguageItem) {
				return locale.equals(((LanguageItem) obj).locale);
			}
			return false;
		}

	}

}
